CMake从入门到实践

您所在的位置:网站首页 cmake pdf 入门 精通 CMake从入门到实践

CMake从入门到实践

2023-07-03 12:23| 来源: 网络整理| 查看: 265

文章目录 CMake从入门到实践CMake 是什么?特点及优势CMake基本语法语法规则变量常见变量 控制语句if语句while语句foreach语句代码块常用指令更多信息 工程实践了解Make从程序的工作过程说起静态库动态库总结工程应用 工程实践如何生成可执行文件,动态库,静态库?示例 如果可执行文件依赖源码生成的库文件呢?即内部库的依赖如何链接一个外部的库呢?利用绝对路径的方式导入用find_library的方式导入用find_package的方式导入find_package的原理

CMake从入门到实践 CMake 是什么?

全称Cross Platform Make,起初为了跨平台需求,而后不断完善并广泛使用

一门语言

一款优秀的工程构建工具

特点及优势

开放源代码,具有BSD许可

跨平台,支持Linux, Mac和Windows等不同操作系统

编译语言简单,易用,简化编译构建过程和编译过程

编程高效,可扩展

CMake基本语法 语法规则

变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名

指令(参数 1 参数 2…) 参数使用括弧括起,参数之间使用空格或分号分开。

指令是大小写无关的,参数和变量是大小写相关的。

SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”)。

文件名中间包含了空格 SET(SRC_LIST “fun nc.c”)

可以忽略掉 source 列表中的源文件后缀

变量

变量引用:使用${}进行变量值的引用。

但在 IF 等语句中,是直接使用变量名而不通过${}取值

自定义变量: 两种方式

隐式定义:PROJECT 指令隐式的定义project_BINARY_DIR 和project_SOURCE_DIR 两个变量显式定义:使用 SET 指令 比如: SET(HELLO_SOURCE_PATH main. c) 常见变量 序号语句注释1PROJECT_BINARY_DIR、PROJECT_SOURCE_DIR CMAKE_BINARY_DIR、CMAKE_SOURCE_DIR工程目标文件目录 工程源文件目录2CMAKE_CURRENT_BINARY_DIR CMAKE_CURRENT_SOURCE_DIR指当前处理的 CMakeLists.txt所在的路径**。**3CMAKE_CURRENT_LIST_FILE CMAKE_CURRENT_LIST_LINE输出调用这个变量的 CMakeLists.txt 的路径及行号4_BINARY_DIR _SOURCE_DIRproject name 工程目标文件 project name 源目标文件5EXECUTABLE_OUTPUT_PATH最终目标二进制文件存放目录6LIBRARY_OUT_PATH最终目标库文件存放目录7CMAKE_INSTALL_PREFIX目标文件安装目录,默认目录为 /usr/local/bin8CMAKE_MODULE_PATH定义自己的 CMake 模块所在路径9PROJECT_NAME返回通过 PROJECT 指令定义的值10CMAKE_INCLUDE_CURRENT_DIR自动添加****CMAKE_CURRENT_BINARY_DIR 和****CMAKE_CURRENT_SOURCE_DIR 添 加到当前 CMakeLists.txt 处理**。**11CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE将工程提供的头文件目录始终至于 系统头文件目录前面12CMAKE_MAJOR_VERSION CMAKE_MINOR_VERSION CMAKE_PATCH_VERSIONCMake **主版本号,**2.4.6 中的 2 CMake **次版本号,**2.4.6 中的 4 CMake **的补丁等级,**2.4.6 中的 613CMAKE_SYSTEM CMAKE_SYSTEM_NAME CMAKE_SYSTEM_VERSION CMAKE_SYSTEM_PROCESSOR系统名称,如 Linux-2.6.26 Linux 2.6.26 I38614UNIX WIN32在所有的类 UNIX 平台值为 **TRUE,**包括 MacOS 和 Cygwin 在所有的 WIN32 平台值为 TRUE,包括Cygwin15CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS开关选项,用来控制 if else 的书写方式16BUILD_SHARED_LIBS开关,默认为静态库17CMAKE_C_FLAGS CMAKE_CXX_FLAGS设置 C 编译选项 设置 C++ 编译选项 控制语句 if语句 #IF 指令,基本语法为: IF(expression) COMMAND1(ARGS) ELSE(expression) COMMAND2(ARGS) ENDIF(expression) # 另外一个指令是 ELSEIF,总体把握一个原则,凡是出现 IF 的地方一定要有对应的ENDIF.出现 ELSEIF 的地方,ENDIF 是可选的。 IF(var) # 如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或_NOTFOUND 时,表达式为真。 IF(NOT var ) # 与上述条件相反。 IF(var1 AND var2) # 当两个变量都为真是为真。 IF(var1 OR var2) # 当两个变量其中一个为真时为真。 IF(COMMAND cmd) # 当给定的 cmd 确实是命令并可以调用是为真。 IF(EXISTS dir) # 当目录名存在时为真 IF(EXISTS file) # 当文件名存在时为真。 IF(file1 IS_NEWER_THAN file2) # 当 file1 比 file2 新,或者 file1/file2 其中有一个不存在时为真,文件名请使用完整路径。 IF(IS_DIRECTORY dirname) # 当 dirname 是目录时,为真。 IF(variable MATCHES regex) IF(string MATCHES regex) # 当给定的变量或者字符串能够匹配正则表达式 regex 时为真。 while语句 #WHILE 指令的语法是: WHILE(condition) COMMAND1(ARGS) ENDWHILE(condition) foreach语句 #FOREACH 指令的使用方法有三种形式: #1,列表 FOREACH(loop_var arg1 arg2 ...) COMMAND1(ARGS) COMMAND2(ARGS) ENDFOREACH(loop_var) #举例如下: FOREACH(F ${SRC_LIST}) MESSAGE(${F}) ENDFOREACH(F) #2,范围 从 0 到 total 以1为步进 FOREACH(loop_var RANGE total) ENDFOREACH(loop_var) # 举例如下: FOREACH(VAR RANGE 10) MESSAGE(${VAR}) ENDFOREACH(VAR) # 0 1 2 3 4 5 6 7 8 9 10 #3,范围和步进 从 start 开始到 stop 结束,以 step 为步进, FOREACH(loop_var RANGE start stop [step]) ENDFOREACH(loop_var) # 举例如下 FOREACH(A RANGE 5 15 3) MESSAGE(${A}) ENDFOREACH(A) #5 8 11 14 代码块 macro/function语法 macro( [arg1 [arg2 [arg3 ...]]]) COMMAND1(arg1) ... endmacro([name]) function( [arg1 [arg2 [arg3 ...]]]) COMMAND1(arg1) ... function([name]) macro/function内部可用变量 变量说明ARGV#ARGV0为第一个参数,ARGV1为第二个参数,依次类推ARGV定义宏(函数)时参数为2个,实际传了4个,则ARGV代表实际传入的两个ARGN定义宏(函数)时参数为2个,实际传了4个,则ARGN代表剩下的两个ARGC实际传入的参数的个数 两者的区别

宏的ARGN、ARGV等内部变量不能直接在if语句和foreach(…IN LISTS…)语句中使用

常用指令 序号指令注释1PROJECT(project name [CXX][C][Java])定义工程名称(工程名与生成的目标文件名称是没有任何关系的)。此条指令隐含了两个变量 _BINARY_DIR _SOURCE_DIR2SET(var [value] [cache type docstring[force]])自定义变量指令 set( SRC_LIST main.cpp hello.cpp) set(SRC_LIST “main.cpp” “test.cpp”)3MASSEGE([SEND_ERROR|STATUS|FATAL_ERROR] “massage to display” …)SEND_ERROR:产生错误,生成过程被跳过 STATUS:输出前缀为—的信息FATAL_ERROR:立即终止所有 CMake过程4$ENV{NAME} SET(ENV{变量名}值)调用系统环境变量 设置环境变量值5INCLUDE(file [OPTIONAL]) INCLUDE(module [OPTIONAL])用来载入 CMakeLists.txt 文件或者 CMake 模块6CMAKE_MINIMUM_REQUIRED(VERSION version_num [FATAL_ERROR])检查 CMake 版本,若不满足,产生 错误提示或退出7SET_TARGET_PROPERTIES(target1 target2 … PROPERTIES prop1 value prop2 value2 …)设置目标输出的名字及属性8GET_TARGET_PROPERTIES(VAR target property)获取目标的属性9ADD_EXECUTABLE(target source_file…)增加可执行目标文件, target 由 source_file 生成10ADD_LIBRARY(libname [SHARED|STATIC| MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)STATIC 为静态链接库,SHARED 为动态链接库,而 MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。11ADD_DEFINITIONS向编译器添加-D 定义12ADD_DEPENDENCIES(target_name depend_target1 depend_target)定义 target 依赖的其他 target13ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE FROM ALL])增加子目录14INCLUDE_DIRECTORIES/TARGET_INCLUDE_DIRECTORIES包含目录15LINK_DIRECTORIES/TARGET_LINK_DIRECTORIES链接目录16LINK_LIBRARIES /TARGET_LINK_LIBRARIES库文件17INSTALL(TARGETS targets [ [ARCHIVE|LIBRARY|RUNTIME] [DESTINATION ] [PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [OPTIONAL] ][…]) 安装目标文件 ARCHIVE 静态库文件LIBRARY 动态库文件RUNTIME 可执行文件 DESTINATION 定义安装路径,如果是绝对路径则覆盖了CMAKE_INSTALL_PREFIX,否则是指相对 CMAKE_INSTALL_PREFIX 的相对路径18INSTALL(FILES files [ [DESTINATION ] [PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [OPTIONAL] ][…]) 安装普通文件 可以指定权限,如果不指定,则默认是 644 权限19INSTALL(DIRECTORY dirs [ [DESTINATION ] [FILE_PERMISSIONS permissions…] [DIRECTORY_PERMISSIONS permissions…] [USE_SOURCE_PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [[PATTERN | REGEX ] [EXCLUDE] [PERMISSIONS permissions…]] […]) 安装目录 可以指定权限,如果不指定,则默认是 644 权限20EXEC_PROGRAM(program [ARGS args] [OUTPUT_VARIABLE var] [RETURN_VALUE value])ARGS 用于添加参数 OUTPUT_VARIABLE 用于获取命令输出 RETURN_VALUE 用于获取返回值21FILE 指令 FILE(WRITE filename“message” …) FILE(APPEND filename “message” …) FILE(READ filename variable) FILE(GLOB variable [RELATIVE path] [globing expressions]…) FILE(GLOB_RECURSE variable [RELATIVE path] [globing expressions]…) FILE(REMOVE [directory]…) FILE(REMOVE_RECURSE [directory]…) FILE(MAKE_DIRECTORY [directory]…) FILE(RELATIVE_PATH variable directory file) FILE(TO_CMAKE_PATH path result) FILE(TO_NATIVE_PATH path result)写文件 添加内容到文件 读文件   移除目录 递归移除目录创建目录22FIND指令 FIND_FILE(name1 path1 path2 …) FIND_LIBRARY(name1 path1 path2 …) FIND_PATH(name1 path1 path2 …) FIND_PROGRAM(name1 path1 path2 …) FIND_PACKAGE( [major.minor] [QUITE] [NO_MODULE][[REQUIRED|COMPONENTS] [components…]])VAR 变量name1 代表找到的文件全路径,包含文件名 VAR 变量name2 代表找到的文件全路径,包含库文件名 VAR 变量代表包含这个文件的路径VAR 变量代表包含这个程序的全路径 更多信息

去CMake官网https://cmake.org/cmake/help/v3.22/查找。如list。

在这里插入图片描述

工程实践 了解Make 内部构建与外部构建 内部构建:源文件与生成的临时文件混在一起外部构建:源文件与生成的临时文件分开

configure、generate和cmake

configure配置CMake里面的变量

generate 产生具体的文件

cmake 两命令一起

CMake产生了什么?

makefile.sln 从程序的工作过程说起

我们知道从我们的源代码到最终的二进制可执行文件(可执行程序,动态库,静态库),需要四个大的步骤:

预编译编译汇编链接

具体细节可以参考《程序员的自我修养》,大体流程如下图:

在这里插入图片描述

静态库、动态库的区别来自链接阶段。如何处理库,链接成可执行的程序?这是链接器的工作,但CMake要为链接器提供库的信息,如何哪些信息,如何提供是我们要思考的。我们先来了解一下静态库和动态库吧。

静态库 定义:

链接阶段,库中目标文件所含的所有将被程序使用的函数的机器码,被复制到最终的可执行文件中。因此对应的链接方式称为静态链接。

特点: 静态库对函数库的链接是放在编译时期完成的;程序在运行时与函数库再无瓜葛,移植方便;运行效率相对快;占用磁盘和内存空间,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

在这里插入图片描述

局限性 空间浪费是静态库的一个问题;静态库对程序的更新、部署和发布页会带来 麻烦;如果静态库lib更新了,所以使用它的应用程 序都需要重新编译、发布给用户;对于玩家来说,可能是一个很小的改动,却 导致整个程序重新下载,全量更新。 动态库 定义:

程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入

特点: 可执行文件只包含它需要的函数的引用表,而不是所有的函数代码;只有在程序执行时, 那些需要的函数代码才被拷贝到内存中;动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

在这里插入图片描述

优缺点:

对库函数的链接载入推迟到程序运行的时期;

实现进程之间的资源共享,也称为共享库;

一些程序升级变得简单,增量更新;

但依赖的模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出 错误信息,依赖性强。

总结

静态库是牺牲了空间效率,换取了时间效率;

动态库是牺牲了时间效率,换取了空间效率。

工程应用 一般在比较固定参数的工程项目中,如底层驱动逻辑比较稳定,会考虑静态库的使用;在需要多次调试优化版本中,比如PUBG(吃鸡)的游戏平衡体验更新,会考虑动态库的使用。 工程实践 如何生成可执行文件,动态库,静态库? 可执行文件=> add_executable库文件=>add_library 示例 cmake_minimum_required(VERSION 3.1.6) project(test) include_directories(include) add_executable(test main.cpp) # test.exe add_library(test_lib STATIC main.cpp) # .lib/.a add_library(test_lib_dll SHARED main.cpp) # .dll/.so # target 不能同名 但是需要静态库和动态库同名 set_target_properties(test_lib PROPERTIES OUT_PUT "test") set_target_properties(test_lib_dll PROPERTIES OUT_PUT "test") 如果可执行文件依赖源码生成的库文件呢?即内部库的依赖 先add_lib在target_link_library 如何链接一个外部的库呢? 我们需要什么? 头文件库文件 链接库的名字库的接口文件即头文件库所在位置 导入的方法有哪几种? 绝对路径find_libraryfind_package 利用绝对路径的方式导入 include目录link目录link库文件 cmake_minimum_required(VERSION 3.1.6) project(test) set(d_lib D:/lib/) include(${d_lib}/include) add_executable(test main.cpp) target_link_libraries(test ${d_lib}/lib/d_lib.lib)

依赖路径必须是绝对路径,可移植性和通用性不强; 导入方法太笨,有没有更有效的方式来管理库的依赖 ??

用find_library的方式导入 Include目录find_library cmake_minimum_required(VERSION 3.1.6) project(test) set(d_lib ../lib/) include(${d_lib}/include) find_library(test_lib ${d_lib}/lib/) add_executable(test main.cpp) target_link_libraries(test ${test_lib}) 名为_ROOT的cmake变量或环境变量。 CMake3.12新增。设定CMP0074 Policy来关闭。如果定义了_DIR变量,那么_ROOT 不起作用。 cmake特定的缓存变量: CMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH可以通过设定NO_CMAKE_PATH来关闭这一查找顺序 cmake特定的环境变量 _DIRCMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH可以通过NO_CMAKE_ENVIRONMENT_PATH来跳过。

HINT字段指定的路径

搜索标准的系统环境变量PATH。

其中如果是以/bin或者/sbin结尾的,会自动转化为其父目录。通过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。 设定为当前系统定义的cmake变量: CMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_FRAMEWORK_PATHCMAKE_SYSTEM_APPBUNDLE_PATH通过设定NO_CMAKE_SYSTEM_PATH来跳过。 从PATHS字段指定的路径中查找。 用find_package的方式导入 CMAKE_MOUDLE_PATH/CMAKE_INSTALL_PREFIXfind_package cmake_minimum_required(VERSION 3.1.6) project(test) set(CMAKE_MODULE_PATH ../lib/) #set(CMAKE_INSTALL_PREFIX ../lib/) find_package(test_lib REQUIRED) add_executable(test main.cpp) target_link_libraries(test ${test_lib}) find_package的原理

前提知识:

由于cmake不提供搜索库的方法,不会对库本身的环境变量进行设置。两种模式:模块模式和配置模式,分为module和config;指令按照优先级顺序在指定路径查找Findxxx.cmake和xxxConfig.cmake;cmake能够找到这两个文件中的任何一个,我们都能成功使用该库。

搜索路径:

Moudle: CMAKE_MODULE_PATH(自己指定)cmake module directory(cmake安装时的Modules目录 Config:

https://cmake.org/cmake/help/v3.22/command/find_package.html?highlight=find_package

名为_ROOT的cmake变量或环境变量。 CMake3.12新增。设定CMP0074 Policy来关闭。如果定义了_DIR变量,那么_ROOT 不起作用。 cmake特定的缓存变量: CMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH可以通过设定NO_CMAKE_PATH来关闭这一查找顺序 cmake特定的环境变量 _DIRCMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH可以通过NO_CMAKE_ENVIRONMENT_PATH来跳过。

HINT字段指定的路径

搜索标准的系统环境变量PATH。

其中如果是以/bin或者/sbin结尾的,会自动转化为其父目录。通过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。 存储在cmake的"User Package Registry"(用户包注册表)中的路径。 通过设定NO_CMAKE_PACKAGE_REGISTRY来跳过设定CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为true, 来避开。 设定为当前系统定义的cmake变量: CMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_FRAMEWORK_PATHCMAKE_SYSTEM_APPBUNDLE_PATH通过设定NO_CMAKE_SYSTEM_PATH来跳过。 在cmake的"System Package Registry"(系统包注册表)中查找。 通过设定NO_CMAKE_SYSTEM_PACKAGE_REGISTRY跳过。设定CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为true,来避开。 从PATHS字段指定的路径中查找。

cmake的"User Package Registry"(用户包注册表)中的路径。

通过设定NO_CMAKE_PACKAGE_REGISTRY来跳过设定CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为true, 来避开。 设定为当前系统定义的cmake变量: CMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_FRAMEWORK_PATHCMAKE_SYSTEM_APPBUNDLE_PATH通过设定NO_CMAKE_SYSTEM_PATH来跳过。 在cmake的"System Package Registry"(系统包注册表)中查找。 通过设定NO_CMAKE_SYSTEM_PACKAGE_REGISTRY跳过。设定CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为true,来避开。 从PATHS字段指定的路径中查找。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3